# ==============================
# _*_ coding:utf-8 _*_
# _*_ by:zizle     _*_
# _*_ at:20230415  _*_
# ==============================
import pathlib
import datetime
import traceback
from fastapi import FastAPI
from fastapi.requests import Request
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from fastapi.exceptions import RequestValidationError, StarletteHTTPException

from response import APIResponse
from settings import FILE_STORAGE, WORK_FILE_STORAGE
from errors import LoginExpireError
from db_utils import RedisConnection, BaseDatabaseConnection
from constant.variety import VarietyConstants

from router import v1_api_router


from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 注册pdf字体
pdfmetrics.registerFont(TTFont('SyH_N', f'{FILE_STORAGE}ttf/SourceHanSans-Normal.ttf'))  # 注册字体
pdfmetrics.registerFont(TTFont('SyH_B', f'{FILE_STORAGE}ttf/SourceHanSans-Bold.ttf'))


class App:
    def __init__(self):
        self.app = FastAPI()
        self.fatal_folder = pathlib.Path('log/Fatal')
        self.error_folder = pathlib.Path('log/Error')

    def get_app(self):
        self.doing_before_created()    # 处理一些事情
        self.mount_static()            # 挂载静态文件
        self.add_middlewares()         # 添加中间件
        self.add_exception_handlers()  # 全局错误处理
        self.add_routers()             # 路由
        self.init_redis()
        return self.app

    def mount_static(self):
        self.app.mount('/appFiles/', StaticFiles(directory=FILE_STORAGE), name='appFiles')
        self.app.mount('/fileStorage/', StaticFiles(directory=WORK_FILE_STORAGE),  name='annexFiles')

    def add_middlewares(self):
        # CORS
        self.app.add_middleware(CORSMiddleware,
                                allow_origins=['*'], allow_credentials=False,
                                allow_methods=['*'], allow_headers=['*'])

    def add_exception_handlers(self):
        self.app.middleware('http')(self.catch_exceptions_middleware)
        self.app.add_exception_handler(LoginExpireError, handler=self.login_expire_exception)
        self.app.add_exception_handler(RequestValidationError, handler=self.request_validation_exception)
        self.app.add_exception_handler(StarletteHTTPException, handler=self.http_exception)

    @staticmethod
    def login_expire_exception(request: Request, exc):
        return APIResponse.login_expire_error()

    def request_validation_exception(self, request: Request, exc: RequestValidationError):
        print('HTTP: {} 422 ERROR: {}'.format(request.url, exc.errors()))
        desc = ''
        error_types = {
            'value_error.missing': '是必须的',
            'type_error.integer': '需为整数类型',
        }
        for e in exc.errors():
            loc = list(e.get('loc', ('', '')))
            if len(loc) < 2:
                loc.append('')
            desc += '{}参数`{}`{};'.format(loc[0], loc[1], error_types.get(e.get('type'), e.get('msg', '')))
        if not desc:
            desc = '参数错误.'
        else:
            desc = desc[:-1] + '.'
        log_file = self.error_folder.joinpath('{}.log'.format(self.now_time(is_time=False)))
        with open(log_file, 'a+', encoding='utf8') as ef:
            ef.write('Validate_Error:{}:{}\n'.format(self.now_time(), desc))
        return APIResponse.validate_error(detail=desc)

    def http_exception(self, request: Request, exc: StarletteHTTPException):
        print('HTTP: {} ERROR: {}'.format(request.url, exc.detail))
        desc = exc.detail
        log_file = self.error_folder.joinpath('{}.log'.format(self.now_time(is_time=False)))
        with open(log_file, 'a+', encoding='utf8') as ef:
            ef.write('Http_Exception:{}:{}\n'.format(self.now_time(), desc))
        return APIResponse.http_error(detail=desc, code=exc.status_code)

    async def catch_exceptions_middleware(self, request: Request, call_next):
        try:
            return await call_next(request)
        except Exception as e:
            log_file = self.fatal_folder.joinpath('{}.log'.format(self.now_time()))
            with open(log_file, 'w', encoding='utf8') as ff:
                ff.write(traceback.format_exc())
            return APIResponse.server_error(detail='`{}` ERROR: {}'.format(request.url, e))

    def doing_before_created(self):
        # 1 创建重大错误文件夹
        if not self.fatal_folder.exists():
            self.fatal_folder.mkdir(parents=True)
        if not self.error_folder.exists():
            self.error_folder.mkdir(parents=True)

    @staticmethod
    def now_time(is_time=True, tsep=''):
        fot = '%Y-%m-%d_%H{}%M{}%S'.format(tsep, tsep) if is_time else '%Y-%m-%d'
        return datetime.datetime.now().strftime(fot)

    def add_routers(self):
        self.app.include_router(v1_api_router, prefix='/v1')

    # 设置品种名称
    @staticmethod
    def init_redis():
        with BaseDatabaseConnection() as conn1:
            conn1.cursor.execute("SELECT name,code FROM ruizhi_variety;")
            variety_list = list(conn1.cursor.fetchall())
            variety_dict = {v['code']: v['name'] for v in variety_list}
            with RedisConnection() as conn2:
                conn2.set_variety_names(variety_dict)
        variety_dict['G_HGJR'] = '宏观金融'
        variety_dict['G_HGNY'] = '化工能源'
        variety_dict['G_JSHS'] = '金属黑色'
        variety_dict['G_NYCP'] = '农业产品'
        VarietyConstants.set_variety_dict(variety_dict)  # 设置品种全局变量
        print('设置Redis数据完成!')
